import sys,wx

if __name__=="__main__":
	if '..' not in sys.path: sys.path.append('..')
	
from base import *

import fm
	
Settings=fm.Settings
EventBus=fm.EventBus

class LineModStatusModel:
	
	EVTMSK_ADD_LINES,\
	EVTMSK_DEL_LINES,\
	EVTMSK_CHG_LINES,\
	EVTMSK_RESET       = mask_set(4)
	
	UNTOUCHED, CHANGED_AND_SAVED, UNSAVED = range(3)

	def __init__(me,doc):
		me.doc           = doc
		me.line_statuses = []
		
		me.wire_events()
		
		n = me.doc.Edit.GetLineCount()
		
		assert n, "this is never zero, not even for fully empty files"
		
		me.line_statuses = [me.UNTOUCHED] * n
		
	def wire_events(me):
		fm.EventBus.AddBroadcaster(me)
		
		fm.EventBus.ListenFor(me.doc, me.on_saved,        me.doc.EVTMSK_SAVED)
		fm.EventBus.ListenFor(me.doc, me.on_textmodified, me.doc.EVTMSK_TEXTMODIFIED)
	
	def __getitem__(me,i):
		return me.line_statuses[i]
	
	def __len__(me):
		return len(me.line_statuses)
		
	def GetRangeStatus(me,i,j): # like python ranges, i inclusive, j exclusive
		rt = me.UNTOUCHED
		for k in range(i,j):
			if me.line_statuses[k]==me.CHANGED_AND_SAVED:
				rt=me.CHANGED_AND_SAVED
			elif me.line_statuses[k]==me.UNSAVED:
				rt=me.UNSAVED
				break
		return rt
		
	def change_line_n_broadcast(me,i):
		if me.line_statuses[i] == me.UNSAVED:
			return
		
		me.line_statuses[i] = me.UNSAVED
						
		fm.EventBus.Broadcast(me, me.EVTMSK_CHG_LINES, (i,i+1))
			
	def on_textmodified(me):
		beg,n = fm.EventBus.GetEventData()
		
		#print i,n
			
		end = beg+abs(n)
			
		if   n < 0:
						
			# "change" the line where the cursor will be
			me.change_line_n_broadcast(end)
			
			del me.line_statuses[beg:end]
							
			fm.EventBus.Broadcast(me, me.EVTMSK_DEL_LINES, (beg,end))
			
		elif n > 0:
			
			me.change_line_n_broadcast(beg)
			
			me.line_statuses[beg+1:beg+1] = [me.UNSAVED] * n
			
			fm.EventBus.Broadcast(me, me.EVTMSK_ADD_LINES, (beg+1,end+1))			
			
		else: # n==0
		
			me.change_line_n_broadcast(beg)
				
	def on_saved(me):
				
		for i in range(len(me.line_statuses)):
			
			if me.line_statuses[i] <> me.UNSAVED:
				continue
			
			me.line_statuses[i] = me.CHANGED_AND_SAVED
		
		fm.EventBus.Broadcast(me, me.EVTMSK_CHG_LINES, (0,len(me.line_statuses)))

Settings['doc.line_mod_status_margin_width']=10

Settings['colors.changed_and_saved']=Settings['modification.saved_color'] # ??
Settings['colors.unsaved']=Settings['modification.unsaved_color'] # ??
		
class DocMarginView:
	def __init__(me,doc,model):
		me.doc   = doc
		me.model = model
		me.edit  = doc.Edit
		
		me.wire_events()
		
		me.edit.SetMarginType(me.doc.MARGIN__LINE_MOD_STATUS, wx.stc.STC_MARGIN_SYMBOL)
		
		me.reconfigure(init=True)
		
	def wire_events(me):
		fm.EventBus.ListenFor(me.model, me.on_model_change)		
		
	def reconfigure(me,init=False):
		me.edit.SetMarginWidth(me.doc.MARGIN__LINE_MOD_STATUS,Settings['doc.line_mod_status_margin_width'])
		
		color=Settings['colors.changed_and_saved']
		me.edit.MarkerDefine(me.doc.MARKER__CHANGED_AND_SAVED, wx.stc.STC_MARK_FULLRECT, color, color)
		
		color=Settings['colors.unsaved']
		me.edit.MarkerDefine(me.doc.MARKER__UNSAVED, wx.stc.STC_MARK_FULLRECT, color, color)
				
		me.refresh_whole_margin()
		
	def on_model_change(me):
		evtmask = fm.EventBus.GetEventMask()
		i,j     = fm.EventBus.GetEventData()
		
		if   evtmask == me.model.EVTMSK_CHG_LINES:
			for k in range(i,j):
				me.show_status(k,me.model[k])
				
		elif evtmask == me.model.EVTMSK_ADD_LINES:
			#print 'on_model_change\n\trange',i,j
			for k in range(i,j):
				me.show_status(k,me.model[k])
								
		elif evtmask == me.model.EVTMSK_DEL_LINES:
			# ?? there is some obob confusion with line deletions, for now play it safe and do a full refresh
			me.refresh_whole_margin()
			
		else:
			raise RuntimeError("Unexpected event mask %s" % evtmask)
			
	def refresh_whole_margin(me):
		for k in range(len(me.model)):
			me.show_status(k,me.model[k])
	
		
	def show_status(me,line,status):
		me.edit.MarkerDelete(line,-1) # erases all markers
		#print 'show_status\n\t',line,status
		if   status == me.model.CHANGED_AND_SAVED:
			me.edit.MarkerAdd(line,me.doc.MARKER__CHANGED_AND_SAVED)
		elif status == me.model.UNSAVED:
			me.edit.MarkerAdd(line,me.doc.MARKER__UNSAVED)

def bit_rotted_test():
	''
	"""
	>>> md = LineModStatusModel(5)
	>>> md.get_line_data()
	[0, 0, 0, 0, 0]
	>>> md.chg_line(2)
	>>> md.broadcast()
	>>> md.get_status_for_range(1,3)
	2
	>>> md.file_saved()
	>>> md.broadcast()
	>>> md.get_status_for_range(1,3)
	1
	>>> md.chg_line(1)
	>>> md.broadcast()
	>>> md.get_status_for_range(1,3)
	2
	>>> md.file_saved()
	>>> md.broadcast()
	>>> md.get_line_data()
	[0, 1, 1, 0, 0]
	>>> md.ins_lines(1,3)
	>>> md.broadcast()
	>>> md.get_line_data()
	[0, 2, 2, 2, 1, 1, 0, 0]
	>>> md.del_lines(1,6)
	>>> md.broadcast()
	>>> md.get_line_data()
	[0, 0, 0]
	>>> for i in range(3):
	... 	md.chg_line(i)
	... 	md.broadcast()
	>>> md.reset_all_to_untouched()
	>>> md.broadcast()
	>>> md.get_line_data()
	[0, 0, 0]
	
	See how the events are
	>>> def hd(): print EventBus.get_event_mask(),EventBus.get_event_data()
	>>> EventBus.connect(hd,md)
	>>> md.ins_lines(1,3)
	>>> md.broadcast()
	1 (1, 4)
	"""
	
if __name__=="__main__":
	import doctest
	doctest.testmod()